#ifndef __pty_kenny_asdfdkfjiwooi3w3__
#define __pty_kenny_asdfdkfjiwooi3w3__

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <asm/ioctl.h>
#include <termios.h>

#ifndef IIOCGWINSZ
#   include <sys/ioctl.h>
#endif

#include <cstdlib>
#include <string>
#include <vector>
#include <memory>

#include <hude/aio/context.hpp>

namespace pty_8823i
{
    /*********************************************************************
     * @brief 伪终端句柄对象
     */
    class _pt_t :
        public hude::aio::e2e::_peer_t
    {
    public:
        _pt_t( int handle ) : handle_( handle ) {}
        ~_pt_t() override { close(); }

        void close() override;

        pid_t setsid();

        int tcgetattr( struct termios & t ) const;
        int tcsetattr( const struct termios & t, int o = TCSANOW );
        int setwinsz( const struct winsize & );
        int dup();
        int echo( bool );

        // 获取原始句柄
        int native() const override { return handle_; }
        // 获取原始句柄
        operator int() const { return handle_; }

        int read( void*, size_t );
        int write( const void*, size_t );

        int read ( std::vector< uint8_t >& );
        int write( const std::vector< uint8_t >& );

        int recv( void* p, size_t n ) override { return read( p, n ); }
        int send( const void* p, size_t n ) override { return write( p, n ); }

    private:
        _pt_t( const _pt_t& );
        _pt_t& operator=( const _pt_t& );

    private:
        int handle_;

    };

    /*********************************************************************
     * @brief 伪终端句柄对象指针
     */
    using pt_t = std::shared_ptr< _pt_t >;

    /*********************************************************************
     * @brief 伪终端
     */
    class pty_t
    {
    public:
        pty_t( int flag = O_RDWR | O_NOCTTY ) :
            master_( init( flag ) ) {}
        ~pty_t() { deinit(); }

        // 获取伪终端设备结点名
        const char* ptsname() const;
        // 获取原始句柄
        int native() const { return master_->native(); }

        pt_t master();
        pt_t slave();

        // 判断是否有效
        operator bool() const { return !!master_; }

    private:
        _pt_t* init( int = O_RDWR | O_NOCTTY );
        void deinit();

    private:
        pt_t master_;
    };

    /*********************************************************************
     * @brief 一对互通的pty
     */
    class pty_pair_t
    {
    public:
        using cb_filter_t = std::function< void( std::vector<uint8_t>& ) >;
        using aio_t = hude::aio::ctx_t<>;

    public:
        pty_pair_t() : bind_( a_.master(), b_.master() ) {}
        ~pty_pair_t() { unbind(); }

        // 获取A端伪终端对象
        pty_t& a() { return a_; }
        // 获取B端伪终端对象
        pty_t& b() { return b_; }

        // 获取A端伪终端对象
        const pty_t& a() const { return a_; }
        // 获取B端伪终端对象
        const pty_t& b() const { return b_; }

        int bind( cb_filter_t filter = cb_filter_t{}, aio_t aio = hude::aio::context_t<>::ctx(), size_t = 255U );

        void unbind();

    private:
        aio_t aio_;
        pty_t a_, b_;
        hude::aio::e2e::_bind_t bind_;
    };

    /*********************************************************************
     * @brief 关闭tty
     */
    inline void _pt_t::close() { ::close( handle_ ); }

    /*********************************************************************
     * @brief 创建会话
     */
    inline pid_t _pt_t::setsid() { return ::setsid(); }

    /*********************************************************************
     * @brief 设置窗口参数
     */
    inline int _pt_t::setwinsz( const struct winsize &args ) { return ::ioctl( handle_, TIOCSWINSZ, args ); }

    /*********************************************************************
     * @brief 获取终端属性
     */
    inline int _pt_t::tcgetattr( struct termios & t ) const { return ::tcgetattr( handle_, &t ); }

    /*********************************************************************
     * @brief 设置终端属性
     */
    inline int _pt_t::tcsetattr( const struct termios & t, int o ) { return ::tcsetattr( handle_, o, &t ); }

    /*********************************************************************
     * @brief 复制句柄
     */
    inline int _pt_t::dup()
    {
        if (::dup2(handle_, STDIN_FILENO) != STDIN_FILENO)
            return -1;

        if (::dup2(handle_, STDOUT_FILENO) != STDOUT_FILENO)
            return -2;

        if (::dup2(handle_, STDERR_FILENO) != STDERR_FILENO)
            return -3;

        return 0;
    }

    /*********************************************************************
     * @brief 设置终端开关回显
     */
    inline int _pt_t::echo( bool enable )
    {
        termios attr;
        auto ret = this->tcgetattr( attr );
        if( ret ) return ret;

        if( enable )
        {
            attr.c_lflag |= ECHO;
        } else {
            attr.c_lflag &= ~( ECHO );
        }
        return this->tcsetattr( attr );
    }

    /*********************************************************************
     * @brief 从tty读数据
     */
    inline int _pt_t::read( void* p, size_t n )
    {
        return ::read( handle_, p, n );
    }

    /*********************************************************************
     * @brief 写数据到tty
     */
    inline int _pt_t::write( const void* p, size_t n )
    {
        return ::write( handle_, p, n );
    }

    /*********************************************************************
     * @brief 从tty读数据
     */
    inline int _pt_t::read( std::vector< uint8_t >& buffer )
    {
        buffer.resize( 255U, 0 );
        auto ret = ::read( handle_, &(buffer[0]), 255U );

        if( ret > 0 )
            buffer.resize( ret );
        else
            buffer.clear();

        return ret;
    }

    /*********************************************************************
     * @brief 写数据到tty
     */
    inline int _pt_t::write( const std::vector< uint8_t >& buffer )
    {
        return ::write( handle_, &(buffer[0]), buffer.size() );
    }

    /*********************************************************************
     * @brief 获取slave文件路径
     */
    inline const char* pty_t::ptsname() const
    {
        return ::ptsname( master_->native() );
    }

    /*********************************************************************
     * @brief 创建伪终端
     */
    inline _pt_t* pty_t::init( int flag )
    {
        int ret = posix_openpt( flag );
        if( ret < 0 ) return nullptr;

        if ( grantpt( ret ) < 0 )
        {
            close( ret );
            return nullptr;
        }
        if ( unlockpt( ret ) < 0 )
        {
            close(ret);
            return nullptr;
        }

        return new _pt_t{ ret };
    }

    /*********************************************************************
     * @brief 关闭伪终端
     */
    inline void pty_t::deinit()
    {
        master_.reset();
    }

    /*********************************************************************
     * @brief 获取master
     */
    inline pt_t pty_t::master() { return master_; }

    /*********************************************************************
     * @brief 获取slave
     */
    inline pt_t pty_t::slave()
    {
        return pt_t{ new _pt_t{ ::open( this->ptsname(), O_RDWR ) } };
    }

    /*********************************************************************
     * @brief 启动虚拟串口
     */
    inline int pty_pair_t::bind( cb_filter_t filter, aio_t aio, size_t siz )
    {
        if( !a_ || !b_ ) return -1000;

        auto ma = a_.master();
        auto mb = b_.master();

        if( ma->echo( false ) || mb->echo( false ) )
        {
            return -1001;
        }

        aio_ = aio;

        bind_.bind( aio_, filter, siz );

        return 0;
    }

    /*********************************************************************
     * @brief 关闭虚拟串口
     */
    inline void pty_pair_t::unbind()
    {
        if( aio_ )
        {
            bind_.unbind( aio_ );

            aio_.reset();
        }
    }

} //namespace pty_8823i

/*********************************************************************
 * 组件导出界面
 */
namespace hude
{
    namespace utils
    {
        using _pt_t         = pty_8823i::_pt_t;
        using pt_t          = pty_8823i::pt_t;
        using pty_t         = pty_8823i::pty_t;
        using pty_pair_t    = pty_8823i::pty_pair_t;
    } //namespace utils
} //namespace hude

#endif //__pty_kenny_asdfdkfjiwooi3w3__

